﻿
/**
* Constructor for QuerySuggester.
*/
function QuerySuggester() {
    this.id = querySuggesters.length;
    querySuggesters[this.id] = this;
}

String.prototype.trim = function() {
    return this.replace(/^\s*/, "").replace(/\s*$/, "");
}

QuerySuggester.prototype.useInlineSuggestion = true;                // Whether or not to display the first suggestion as marked text within the search-box
QuerySuggester.prototype.clearInlineSuggestionBeforeSubmit = true; // Whether or not to remove inline suggestions before submitting

// Public functions

/**
* Initialize the query-suggester.
*
* @param suggestionUrl the url to fetch suggestions from, will append the query string to it
* @param formId        the id of the form that should be submitted if the user clicks a suggestion
* @param tooltipId     the id of the div used to display the suggestsions
*/
QuerySuggester.prototype.initialize = function(suggestionUrl, formId, tooltipId, queryId) {
    this.__enabled = new BackgroundLoader().isEnabled();
    queryId.autoComplete = "off";
    if (this.__enabled) {
        this.__suggestionUrl = suggestionUrl;
        this.__form = this.byId(formId);
        if (!this.__form) {
            this.__enabled = false;
            this.debug("Couldn't find the form, disabling.");
        } else {
            this.__tooltip = this.byId(tooltipId);
            this.__queryId = queryId;
            this.__query = this.byId(queryId);
            if (this.__query) {
                var oldKeyUp = this.getFunctionBody(this.__query.onkeyup);
                var oldKeyDown = this.getFunctionBody(this.__query.onkeydown);
                var suggester = this;
                this.__query.onkeyup = function(e) { if (typeof e != "undefined") event = e; eval(oldKeyUp); suggester.keyUp(event, queryId); };
                this.__query.onkeydown = function(e) { if (typeof e != "undefined") event = e; eval(oldKeyDown); suggester.keyDown(event, queryId); };
            }
        }
    } else {
        this.debug("Unable to instantiate XMLHttpRequest, disabling.");
    }
};

/**
* Set the id of the textarea where debug-output is written. Initially, there is no such
* textarea assigned to the suggester.
*
* @param debugAreaID the id of the textarea to print debug messages to, or boolean false
to disable debugging
*/
QuerySuggester.prototype.setDebugAreaId = function(debugAreaId) {
    this.__debugAreaId = debugAreaId;
};

/**
* Set the id of the input-field to do completion for. Not really neccessary to call, as
* the id is sent to the key handling methods.
*
* @param queryId the id of the search box
*/
QuerySuggester.prototype.setQueryId = function(queryId) {
    this.__queryId = queryId;
    this.__query = this.byId(queryId);
};

// Overridable functions

/**
* Returns the url to visit to obtain suggestions for a query. By default appends the
* query to the suggestionUrl member. Override for different behavior.
*
* @param quer the query for which to fetch suggestions
* @return the url that will return the suggestions
*/
QuerySuggester.prototype.getSuggestionUrl = function(query) {
    return this.__suggestionUrl + query;
};

/**
* Called when the search form should be submitted (usually because of mouse clik on one
* of the suggestions in the list). By default submits the form specified by the formId
* parameter of the initialize() function. Override for different behavior.
*/
QuerySuggester.prototype.submitForm = function() {
    this.__form.submit();
};

// Input event handlers

/**
* Event handler for key up events.
*/
QuerySuggester.prototype.keyUp = function(event, field) {
    if (!this.__enabled) return;
    else if (field != this.__queryId) this.setQueryId(field);
    if (event) {
        if (this.__timeout) {
            clearTimeout(this.__timeout);
            this.__timeout = false;
        }
        if (event.ctrlKey || event.altKey) {
            return;
        }
        var timeout = 100;
        this.__deletePressed = false;
        switch (event.keyCode) {
            case 9: // Tab
            case 27: // Escape
                this.hide();
                return;
            case 8: // Backspace
            case 46: // Delete
                this.__deletePressed = true;
                timeout = 200;
                break;
            case 13: // Enter
            case 16: // Shift
            case 17: // Ctrl
            case 18: // Alt
            case 20: // Caps Lock
            case 33: // Page up
            case 34: // Page down
            case 35: // End
            case 36: // Home
            case 37: // Arrow left
            case 38: // Arrow up
            case 39: // Arrow right
            case 40: // Arrow down
            case 45: // Insert
                return;
            default:
                timeout = 100;
                break;
        }
        this.__index = -1;
        var qc = this;
        this.__timeout = setTimeout(function() { qc.fetchAndDisplaySuggestions(); }, timeout);
    }
};

/**
* Some keys also require keyDown handlers, because they never give a key up event. For
* instance, pressing the 'tab' key will typically cause focus to leave the search input
* box, causing the key up event to be sent to another component.
*/
QuerySuggester.prototype.keyDown = function(event, field) {
    if (!this.__enabled) return;
    else if (field != this.__queryId) this.setQueryId(field);
    if (event && event.keyCode) {
        switch (event.keyCode) {
            case 9: // Tab
                this.hide();
                break;
            case 13: // Enter
                // if (this.__index == -1) this.clearInlineSuggestion();
                break;
            case 38: // Up-arrow
                if (this.__index == 0) {
                    this.__query.value = this.__oldquery;
                }
                if (this.__index >= 0) {
                    this.updateSuggestions(this.__index--, -2);
                }
                break;
            case 40: // Down-arrow
                if (this.__index == -1) {
                    this.__oldquery = this.__query.value.trim();
                }
                if (this.__index < this.__terms.length - 1) this.updateSuggestions(this.__index++, -2);
                break;
        }
    }
};

QuerySuggester.prototype.mouseOver = function(index) {
    if (!this.__enabled) return;

    var previous = this.__mouseIndex >= 0 ? this.__mouseIndex : this.__index;
    this.__mouseIndex = index;
    this.updateSuggestions(-2, previous);
    //this.updateSuggestions(this._mouseIndex, previous)
};

QuerySuggester.prototype.mouseOut = function(index) {
    if (!this.__enabled) return;
    var previous = this.__mouseIndex;
    this.__mouseIndex = -1;
    this.updateSuggestions(-2, previous);
};
// Mihran Mod
QuerySuggester.prototype.mouseClick = function(index) {
    if (!this.__enabled) return;
    if (index >= 0) {

        this.__query.value = this.__terms[index].split('|')[0];
        this.hide();
        //this.submitForm();
    } else {
        this.hide();
    }
};

// Functions for retrieving suggestions

QuerySuggester.prototype.fetchAndDisplaySuggestions = function() {
    var val = this.__query.value;

    // Mihran set min chars to 4 before we do anything
    if (1 == 1) { //val.length >= 3) { 
        if (this.canHandleRanges()) {
            val = val.substring(0, this.getCaretPosition());
        }
        if (val.length == 0) {
            this.hide();
            this.__prev = val;
            return;
        } else {
            val = val.replace(new RegExp("\\\\", "g"), "\\\\");
        }
        this.__prev = val;
        if (this.__cache[val]) {
            this.debug("cache: " + val);
            this.displaySuggestions(this.__cache[val], this.__prev);
        } else {
            this.debug("query: '" + val + "'");
            this.fetchSuggestion(val);
        }
    } else {
        this.noSuggestions();
    }

};

QuerySuggester.prototype.fetchSuggestion = function(query) {
    var qc = this;
    var bl = new BackgroundLoader();
    //this.debug("fetching: '" + query  + "'"); 
    bl.setLoadedCallback(function(content) { qc.parseSuggestions(content); });
    bl.setErrorCallback(function(error) { qc.debug("Couldn't get suggestions:\n" + error); });
    bl.loadUrl(this.getSuggestionUrl(query));
};

// Functions for displaying suggestions

QuerySuggester.prototype.parseSuggestions = function(matches) {
    if (matches == "") return;
    var params = eval(matches);
    if (params.length > 2) {
        this.__cache[params[0]] = params;
    }
    this.displaySuggestions(params, params.length > 2 ? params[0] : "");
};

QuerySuggester.prototype.displaySuggestions = function(matches, query) {
    //this.debug("matches length: '" + matches.length + "'");
    if (matches.length <= 2) {
        this.noSuggestions();
        return;
    }
    if (this.__tooltip && this.__query) {
        matches = this.processSuggestions(matches);
        this.buildSuggestionsHtml(matches);
        this.show();
        //this.showInlineSuggestions(query);
    }
};


QuerySuggester.prototype.toTitleCase = function(str) {
    //lowercase the whole string
    var ls = str.toLowerCase().split(' ');

    //loop through word array
    for (var i = 0; i < ls.length; i++) {
        //alert(ls[i]);
        //replace first letter with uppercase version
        ls[i] = ls[i].charAt(0).toUpperCase() + ls[i].slice(1);
    }
    //rejoin words and return the new string
    return ls.join(' ');
};

QuerySuggester.prototype.buildSuggestionRecord = function(record, terms, i) {
    var text = "";

    var type = 99;
    if (record.type != undefined) {
        type = record.type;
    }

    var redirect = "window.location='?k=" + terms + "'";
    text += "<div class=\"suggestionEntry\" id=\"tooltip_" + i + "\" onmouseover=\"mouseOver(" + this.id + "," + i + ")\" onmouseout=\"mouseOut(" + this.id + "," + i + ")\"  style=\"cursor: pointer\">";
    // outer table
    text += "<table width='100%' id='ccmaster' class='Record'><tr>";

    //start photo
    text += "<td width='60px' onclick=\"" + redirect + "\">";
    if (record.pictureurl != undefined) {
        text += "<img width='60px' src='" + record.pictureurl + "'>";
    }
    text += "</td>";

    //record table
    text += "<td valign='top'>";
    text += "<table width='100%' id='ccvalue'><tr>";
    text += "<td class='Ename' onclick=\"" + redirect + "\">";
    text += record.preferredname;
    text += "</td></tr>";
    text += "<tr><td class='Position' onclick=\"" + redirect + "\">";
    if (record.aboutme != undefined) {
        if (type == 1) { // Club
            text += "Stadium: ";
        } else if (type == 2) { // player
            text += "Team(s): ";
        } else if (type == 3) { // querylog
            text += "Hits: ";
        }
        text += record.aboutme;
    } else {
        text += "N/A";
    }
    text += "</td></tr>";
    // text += "<tr><td>Ranking: " + record.ranking + "</td></tr>";
    text += "</table>";

    text += "</td></tr></table>";

    text += "</div>\n";

    //this.debug("text: '" + text + "'");        
    return text;
};

QuerySuggester.prototype.processSuggestions = function(matches) {
    var text = "";
    var entries = (matches.length - 2) / 2;
    var count = 0
    var result = new Array();

    for (var i = 0; i < entries; i++) {
        var id = matches[2 + (i * 2)];
        var rawdata2 = matches[3 + (i * 2)];
        result[i] = rawdata2;
    }
    //result.sort(this.entryCompare); // sort by rank
    return result;
};

QuerySuggester.prototype.entryCompare = function(a, b) {
    // sort on type, then ranking
    // type = 1 teams, 2 players, 3 query terms
    // sorting is query terms, players, teams
    recorda = QuerySuggester.prototype.buildRecord(a);
    recordb = QuerySuggester.prototype.buildRecord(b);
    typea = 0
    typeb = 0
    if (recorda.type == "1") typea = 3;
    if (recorda.type == "2") typea = 2;
    if (recorda.type == "3") typea = 1;
    if (recordb.type == "1") typeb = 3;
    if (recordb.type == "2") typeb = 2;
    if (recordb.type == "3") typeb = 1;

    if (typea == typeb) {
        if (parseInt(recorda.ranking) < parseInt(recordb.ranking)) return 1;
        if (parseInt(recorda.ranking) > parseInt(recordb.ranking)) return -1;
    } else {
        if (typea < typeb) return -1;
        if (typea > typeb) return 1;
    }
    return 0;
};

QuerySuggester.prototype.buildRecord = function(recordvalue) {
    var rawdata = recordvalue.split('#');
    var record = {}
    for (var k = 0; k < rawdata.length; k++) {
        entry = rawdata[k].split("%");
        //     record[ entry[0] ] =  entry[1];
        record[entry[0]] = rawdata[k].substring(entry[0].length + 1);
    }
    if (record.type == undefined) record['type'] = 99;
    return record;
};

// Mihran Mods
QuerySuggester.prototype.buildSuggestionsHtml = function(matches) {
    var text = "";

    var length = matches.length;
    this.__terms = new Array(length);

    var entries = matches.length
    var count = 0
    var types = new Array();
    types[0] = 0;
    types[1] = 0;
    types[2] = 0;
    types[3] = 0;
    types[99] = 0;

    for (var i = 0; i < entries; i++) {
        /*
        var tmpdata = matches[i]
        var rawdata = tmpdata.split('#')
      
      var record={}
        for (var k=0; k<rawdata.length; k++) {
        entry=rawdata[k].split("%");
        record[ entry[0] ] =  entry[1];
        }
        */
        var record = this.buildRecord(matches[i]);
        type = parseInt(record.type);
        if (types[type] < this.__max[type]) {
            this.__terms[count] = record['preferredname']
            text += this.buildSuggestionRecord(record, this.__terms[count], count);
            count++;
            types[type]++;
        }
    }
    this.__tooltip.innerHTML = text;
};


// Mihran Modes - split('|')[0]
QuerySuggester.prototype.showInlineSuggestions = function(query) {
    if (this.__terms.length > 0 && this.__query.value == query && !this.__deletePressed && this.useInlineSuggestion) {
        this.__original = this.__query.value;
        if (this.canHandleRanges()) {
            this.__query.value = this.__terms[0].split('|')[0];
            this.selectRange(this.__original.length, this.__query.value.length);
        }
    }
};

//Mihran Mo zhilin des - split('|')[0]

QuerySuggester.prototype.updateSuggestions = function(previousIndex, previousMouseIndex) {
    var previous = this.byId("tooltip_" + previousIndex);
    var previousMouse = this.byId("tooltip_" + previousMouseIndex);
    var current = this.byId("tooltip_" + this.__index);
    var currentMouse = this.byId("tooltip_" + this.__mouseIndex);



    if (previous) {
        if (previousIndex != this.__mouseIndex) {
            previous.className = "suggestionEntry";
        }
    }
    if (current) {
        current.className = "suggestionEntryHover";
        if (this.canHandleRanges()) {
            this.__query.value = this.__terms[this.__index];
            //            this.selectRange(this.__original.length, this.__query.value.length);
        }
    }

    //this.debug("Current Mouse: '" + this.__mouseIndex + "'");
    //this.debug("Previous Mouse: '" + previousMouseIndex + "'");

    if (previousMouse && previousMouseIndex != this.__index) {
        previousMouse.className = "suggestionEntry";
        //currentMouse.className = "suugestionEntryHover";
    }
    if (currentMouse) {
        currentMouse.className = "suggestionEntryHover";
    }
};

/**
* Called when the list of matches returned is empty. Default implementation will simply
* hide the list of suggestions. Override for custom behavior.
*/
QuerySuggester.prototype.noSuggestions = function() {
    this.hide();
};

QuerySuggester.prototype.show = function() {
    if (this.__tooltip && this.__query) {
        this.__tooltip.style.left = this.findPosX(this.__query) + "px";
        this.__tooltip.style.top = (this.findPosY(this.__query) + this.__query.offsetHeight) + "px";
        this.__tooltip.style.width = this.__query.offsetWidth + "px";
        this.__tooltip.style.visibility = "visible";
    }
};

QuerySuggester.prototype.hide = function() {
    if (this.__tooltip) {
        this.__terms = new Array();
        this.__tooltip.innerHTML = "";
        this.__tooltip.style.visibility = "hidden";
    }
};

// Text-selection helper-functions

QuerySuggester.prototype.canHandleRanges = function() {
    return this.__query.createTextRange || this.__query.setSelectionRange;
};

QuerySuggester.prototype.selectRange = function(from, to) {
    if (this.__query.createTextRange) {
        var t = this.__query.createTextRange();
        t.moveStart("character", from);
        t.select();
    } else if (this.__query.setSelectionRange) {
        this.__query.setSelectionRange(from, to);
    } else {
        this.debug("Couldn't select range.");
    }
};

QuerySuggester.prototype.getCaretPosition = function() {
    if (document.selection) {
        var range = document.selection.createRange().duplicate();
        range.collapse(true);
        range.moveStart("character", -1000);
        return range.text.length;
    } else if (this.__query.setSelectionRange) {
        return this.__query.selectionStart;
    } else {
        this.debug("Couldn't find caret position.");
        return this.__query.value.length;
    }
};

QuerySuggester.prototype.clearInlineSuggestion = function() {
    if (this.__query && this.canHandleRanges() && this.clearInlineSuggestionBeforeSubmit) {
        this.__query.value = this.__query.value.substring(0, this.getCaretPosition());
    }
};

// General helper-functions

/**
* Helper-function that extracts the body of a function, by converting it to a string, and
* extracting the substring from after the first { to the last }. If the func argument
* isn't a function, an empty string is returned.
*
* @param func the function whose body to extract
* @return the body of func
*/
QuerySuggester.prototype.getFunctionBody = function(func) {
    var body = "";
    if (typeof func == "function") {
        body = func.toString();
        body = body.substring(body.indexOf("{") + 1, body.lastIndexOf("}"));
    }
    return body;
};

QuerySuggester.prototype.findPosX = function(obj) {
    var curleft = 0;
    if (obj.offsetParent) {
        while (obj.offsetParent) {
            curleft += obj.offsetLeft;
            obj = obj.offsetParent;
        }
    } else if (obj.x)
        curleft += obj.x;
    this.debug("curLeft: " + curleft);
    return curleft;
};

QuerySuggester.prototype.findPosY = function(obj) {
    var curtop = 0;
    if (obj.offsetParent) {
        while (obj.offsetParent) {
            curtop += obj.offsetTop
            obj = obj.offsetParent;
        }
    } else if (obj.y) {
        curtop += obj.y;
    }
    this.debug("curtop: " + curtop);
    return curtop;
};

QuerySuggester.prototype.byId = function(id) {
    var element = document.getElementById ? document.getElementById(id) : false;
    return element && element != null ? element : false;
};

QuerySuggester.prototype.debug = function(message) {
    if (this.__debugAreaId) {
        var err = this.byId(this.__debugAreaId);
        if (err) {
            err.value += message + "\n";
        }
    }
};

// Private members

QuerySuggester.prototype.__suggestionUrl = false;   // The url to fetch suggestions from
QuerySuggester.prototype.__form = false;            // The form
QuerySuggester.prototype.__query = false;           // The query input field
QuerySuggester.prototype.__queryId = false;         // The id of the query input field
QuerySuggester.prototype.__tooltip = false;         // The tooltip div
QuerySuggester.prototype.__debugAreaId = false; //"debug";     // Text-area for debugging

QuerySuggester.prototype.__timeout = false;         // The current timeout
QuerySuggester.prototype.__prev = "";               // The previously sent term
QuerySuggester.prototype.__original = "";           // The term before panning into the suggestions
QuerySuggester.prototype.__index = -1;              // The index of the text cursor in the list of suggestions
QuerySuggester.prototype.__mouseIndex = -1;         // The position of the mouse in the list of suggestions
QuerySuggester.prototype.__terms = new Array();     // The list of suggestions
QuerySuggester.prototype.__cache = new Array();     // A result-cache
QuerySuggester.prototype.__enabled = false;
QuerySuggester.prototype.__deletePressed = false;   // Delete or backspace has been pressed


QuerySuggester.prototype.__max = new Array();
QuerySuggester.prototype.__max[1] = 1; // max teams
QuerySuggester.prototype.__max[2] = 5; // max players
QuerySuggester.prototype.__max[3] = 5; // max from querylog
QuerySuggester.prototype.__max[99] = 99; // default max

// Global stuff


// Global array of all instantiated query-suggesters
var querySuggesters = new Array();

// Global mouse-handling function
function mouseOver(id, index) {
    if (id >= 0 && id < querySuggesters.length) {
        querySuggesters[id].mouseOver(index);
        //this.debug("mouseOver: '" + id + "'");
    }
}

// Global mouse-handling function
function mouseOut(id, index) {
    if (id >= 0 && id < querySuggesters.length) {
        querySuggesters[id].mouseOut(index);
    }
}

// Global mouse-handling function
function mouseClick(id, index) {
    if (id >= 0 && id < querySuggesters.length) {
        querySuggesters[id].mouseClick(index);
    } else if (id == -1 && index == -1) {
        for (var i = 0; i < querySuggesters.length; ++i) {
            querySuggesters[i].mouseClick(index);
        }
    }
}
